Java基础知识(十二)
辅助概念
关键字final
Java中,final
被称为终结器,可以用于定义类、方法、属性,具有如下特点:
1、final定义的类不能有子类
final class A{}
class B extends A{} //报错,无法继承A类
开发系统类或者进行一些架构代码开发时使用到final
定义类。
2、final定义的方法不能被方法覆写
父类中某些方法具备某种隐藏的特性,或子类必须使用该方法时,使用final定义,意思是子类不要破坏父类中该方法的结构。
class A{
public final void fun(){}
}
class B extends A{
public void fun(){} // 报错,无法覆写方法
}
3、final定义的变量就成了常量。常量必须在定义时设置好值,且不能修改。
class A {
final double GOOD = 100.0;
public void fun() {
GOOD = 200; // 无法修改常量GOOD的值
}
}
用final定义变量,就可以使用变量名来描述一些数据。开发中常量名均为大写,变量名首个单词小写,之后的单词首字母大写。
4、全局常量:public static final 声明的变量就是全局常量:
public static final String MSG = "MLDN";
static数据存在公共数据区,因此MSG是一个公共常量。
总结:
(1)final定义的类和方法不能继承,不能覆写;
(2)public static final 定义的是全局常量,常量名全部大写。
对象多态性
多态性依赖于方法覆写。
class A {
public void print() {
System.out.println("A中的方法");
}
}
class B extends A {
public void print() {
System.out.println("覆写的方法");
}
}
public class Demo {
public static void main(String[] args) {
B b = new B();
b.print(); // 覆写的方法
}
}
1、多态性分为两种:
(1)方法的多态性
|- 方法重载:同一个方法名,根据传入的参数类型或个数的不同,执行不同方法体;
|- 方法覆写:同一个方法,根据对象所属的子类不同,执行不同方法体。
(2)对象的多态性:指发生在具有继承关系的类中,父类与子类的转换。
|- 向上转型(自动完成):父类 父类对象 = 子类实例化;
|- 向下转型(强制完成):子类 子类对象 = (父类)父类实例化;
范例:向上转型
public class Demo {
public static void main(String[] args) {
A a = new B();
a.print(); // 覆写的方法
}
}
范例:向下转型
public class Demo {
public static void main(String[] args) {
B b = (B) new A(); // 向下转型
b.print(); // 覆写的方法
}
}
2、向上转型:由于子类对象都可以自动向上转型,所以可应用于参数的统一。
class A {
public void print() {
System.out.println("A中的方法");
}
}
class B extends A {
public void print() {
System.out.println("B覆写的方法");
}
}
class C extends A {
public void print() {
System.out.println("C覆写的方法");
}
}
public class Demo {
public static void main(String[] args) {
A a1 = new B(); // 向上转型
A a2 = new C(); // 向上转型
a1.print(); // B覆写的方法
a2.print(); // C覆写的方法
}
}
a1,a2参数统一后,还可以调用子类覆写的方法,即同一个方法针对不同子类可以有不同实现。
3、向下转型:父类发生向上转型后,不能调用子类中自定义的方法。因此当父类要调用子类自定义方法时,需要进行向下转型,将父类对象变为子类对象。
class A {
}
class B extends A {
public void fun() {
System.out.println("自定义方法");
}
}
public class Demo {
public static void main(String[] args) {
A a = new B(); // 向上转型
a.fun(); // 报错,无法调用B自定义方法
}
}
向下转型后,可以调用子类自定义的方法:
class A {
}
class B extends A {
public void fun() {
System.out.println("自定义方法");
}
}
public class Demo {
public static void main(String[] args) {
A a = new B(); // 向上转型
B b = (B) a; // 向下转型
b.fun();
}
}
问题:上述代码中不适用向下转型,直接实例化子类对象就可以直接调用fun(),为什么还要转型?
答:数据的操作分为两步:设置数据(最需要进行参数统一)和取出数据。
public class Demo {
public static void main(String[] args) {
fun(new B()); // 向上转型
}
public static void fun(A a){ // 统一参数
B b = (B) a; // 向下转型
b.fun(); // 调用子类自定义方法
}
}
5、个性化操作在开发中尽量少出现,因为对象的强制转型容易带来安全隐患。
class A {
public void print() {
System.out.println("A.print()");
}
}
class B extends A {
public void print() {
System.out.println("B.print()");
}
}
public class Demo {
public static void main(String[] args) {
A a = new A();
B b = (B) a;
b.print();
}
}
上述代码会报错,Exception in thread "main" java.lang.ClassCastException: A cannot be cast to B at ExDemo.main(ExDemo.java:15)
,表示类转换异常,指的是两个没有关系的类对象强制进行向下转型时发生的异常,因此向下转型存在风险。
为保证转型的安全性,Java提供关键字instanceof
,其返回结果为boolean:对象 instanceof 类
如果某个对象是某个类的实例,则会返回true,反之返回false。
class A {
public void print() {
System.out.println("A.print()");
}
}
class B extends A {
public void print() {
System.out.println("B.print()");
}
}
public class Demo {
public static void main(String[] args) {
A a = new B();
System.out.println(a instanceof A); // true
System.out.println(a instanceof B); // true
if (a instanceof B) {
B b = (B) a;
b.print(); // B.print()
}
}
}
向下转型前应先进行对象的向上转型,建立关系后才能进行向下转型。
总结:
(1)大多数情况只使用向上转型,使得参数统一,便于程序设计;子类尽量覆写方法,而不是自定义方法;
(2)极少情况使用向下转型调用子类的自定义方法,或者不转型。
抽象类
基本概念
1、抽象类是含有抽象方法的类。抽象方法没有方法体,必须用abstract
定义。
abstract class A { // 抽象类
public void fun(){方法体;} // 普通方法
public abstract void print(); // 抽象方法
}
public class Demo {
public static void main(String[] args) {
A a = new A(); // 报错,A是抽象类,无法实例化
}
}
结果显示,无法直接实例化抽象类对象.普通类的对象实例化后,该对象可以调用类中的属性和方法.而抽象类中存在抽象方法,抽象方法没有方法体无法调用,因此无法产生实例化对象.
2、抽象类使用原则:
(1)抽象类必须有子类;
(2)抽象类的子类(非抽象类时)必须覆写抽象类中所有的抽象方法(强制子类进行方法覆写)
(3)抽象类对象的实例化依靠子类完成,采用向上转型方式。
范例:使用抽象类
abstract class A { // 抽象类
public void fun() { // 普通方法
System.out.println("普通方法");
}
public abstract void print(); // 抽象方法
}
class B extends A {
// 强制要求对抽象方法进行覆写,否则会报错
public void print() {
System.out.println("覆写后的抽象方法");
}
}
public class Demo {
public static void main(String[] args) {
A a = new B(); // 向上转型
a.fun();
a.print();
}
}
由上述代码,可知:
(1)抽象类的子类明确要求方法覆写,而普通类没有;
(2)抽象类只比普通类多了抽象方法,其他部分相同;
(3)抽象类对象必须经过向上转型才能实例化;
(4)虽然子类可以继承任何类,但开发中,普通类最好继承抽象类。
使用限制
1、抽象类由于存在属性,因此会有构造方法来初始化属性。子类对象实例化时依然先执行父类构造方法,再调用子类构造方法。
2、抽象类不能使用final定义,因为抽象类必须有子类。
3、抽象外部类不允许使用static
定义,而抽象内部类可以使用static
声明。使用static
定义的抽象内部类相当于抽象外部类,继承时使用外部类.内部类
的形式表示类名。
abstract class A { // 抽象类
static abstract class B {
public abstract void print();
}
}
class X extends A.B {
public void print() {
System.out.println("*****");
}
}
public class Demo {
public static void main(String[] args) {
A.B ab = new X(); // 向上转型
ab.print();
}
}
4、static
定义的方法可以没有实例化对象的情况下直接调用,即使是抽象类中的static方法。
abstract class A { // 抽象类
public static void print() {
System.out.println("static方法");
}
}
public class Demo {
public static void main(String[] args) {
A.print(); // static方法
A a = new A() ; // 报错
}
}
5、有时抽象类只需要一个特定的子类操作,因此可以将该子类定义为该抽象类的内部类。
abstract class A { // 抽象类
public abstract void print();
private static class B extends A { // 内部抽象类子类
public void print() {
System.out.println("Hello");
}
}
public static A getInstance() { // 获取B的实例化对象
return new B();
}
}
public class Demo {
public static void main(String[] args) {
// 客户端得到抽象类对象时,B对其不可见
A a = A.getInstance();
a.print();
}
}
上述设计在系统类库中较为常见,目的是:为用户隐藏不需要知道的子类。
6、观察下述代码:
abstract class A { // 抽象类
// 1. 先调用父类构造
public A() {
this.print();
}
public abstract void print();
}
class B extends A {
private int num = 100;
// 3.调用子类构造,并初始化num = 30.运行结束,未输出初始化后的num
public B(int num) {
this.num = num;
}
// 2. 父类构造调用子类的print,此时num = 0,打印输出。
public void print() {
System.out.println("num = " + num);
}
}
public class Demo {
public static void main(String[] args) {
new B(30); // 4. 结果为0
}
}
在构造方法执行完之前,属性的内容均为其对应的数据类型的默认值。子类在执行构造方法前必先执行父类的构造方法,因为此时子类构造方法还没执行,就调用print()
输出了num的值,所以num为0
.
模板设计模式
要求:设计三个类,通过类描述如下行为:
(1)机器人:充电、工作;
(2)人:吃饭、工作、睡觉;
(3)猪:吃饭、睡觉
思路:定义一个抽象类,具有吃饭、睡觉、工作的抽象方法。根据子类的不同,具体实现抽象方法。
abstract class Action {
public static final int EAT = 1;
public static final int SLEEP = 5;
public static final int WORK = 7;
public void command(int flag) {
switch (flag) {
case EAT:
this.eat();
break;
case SLEEP:
this.sleep();
break;
case WORK:
this.work();
break;
case EAT + WORK:
this.eat();
this.work();
break;
}
}
// 不确定方法中的具体行为,定义为抽象类
public abstract void eat();
public abstract void sleep();
public abstract void work();
}
定义子类:
class Robot extends Action {
public void eat() {
System.out.println("机器人正在补充能量");
}
public void sleep() {
}
public void work() {
System.out.println("机器人正在工作");
}
}
class Human extends Action {
public void eat() {
System.out.println("人正在吃饭");
}
public void sleep() {
System.out.println("人正在睡觉");
}
public void work() {
System.out.println("人正在工作");
}
}
class Pig extends Action {
public void eat() {
System.out.println("猪正在吃饭");
}
public void sleep() {
System.out.println("猪正在睡觉");
}
public void work() {
}
}
范例:测试程序
public class Demo {
public static void main(String[] args) {
fun(new Robot());
fun(new Human());
fun(new Pig());
}
public static void fun(Action act) {
act.command(Action.EAT);
act.command(Action.SLEEP);
act.command(Action.WORK);
}
}
结果显示:子类要实现操作,必须按照Action类的标准。
This blog is under a CC BY-NC-SA 3.0 Unported License
本文链接:http://yov.oschina.io/article/Java/Java Base/Java基础知识(十二)/